<!--
 * @FilePath: chat.vue
 * @@author: wjj
 * @Date: 2025-06-12 20:37:47
 * @@Description: new js file
-->
<template>
  <div class="chat-container">
    <!-- 消息展示区 -->

    <el-tooltip class="box-item" effect="dark" placement="top-start">
      <template #content>
        类型Client：表示向二维码打开的网页发送消息，
        <br />
        后面名字是二维码网页填写的账号（可随意填写，但保证有人登陆此账号）
        <br />
        类型web：表示向此平台发送消息，后面名字是平台登陆的账号（必须真实）
        <br />
        只支持文字类型消息
      </template>
      <el-button
        type="primary"
        style="margin-left: 80%; width: 150px"
        @click="url_to"
        plain
      >
        手机扫码及使用方法?
      </el-button>
    </el-tooltip>
    <div ref="messageContainer" class="message-display">
      <div
        v-for="(msg, index) in messages"
        :key="index"
        :class="['message-bubble', msg.sender]"
      >
        <div class="bubble-content">
          <template v-if="msg.type === 'text'">
            {{ msg.content }}
            <el-button
              type="primary"
              :icon="CopyDocument"
              size="small"
              @click="copyToClipboard(msg.content)"
            />
          </template>
          <template v-else-if="msg.type === 'file'">
            <!-- 图片文件：展示缩略图+下载链接 -->
            <div v-if="isImageFile(msg.fileType)">
              <div class="image-container">
                <el-image
                  :src="msg.url"
                  :preview-src-list="blobUrlStore"
                  :initial-index="blobUrlStore.indexOf(msg.url)"
                  fit="scale-down"
                  style="max-width: 100%"
                />
              </div>
              <!-- <a
                :href="msg.url"
                :download="msg.fileName"
                class="download-link"
                v-if="msg.sender == 'ai'"
              >
                下载图片
              </a> -->
            </div>

            <!-- 非图片文件：仅下载链接 -->
            <div v-else>
              <a
                :href="msg.url"
                :download="msg.fileName"
                target="_blank"
                rel="noopener noreferrer"
              >
                下载文件: {{ msg.fileName }}
              </a>
            </div>
          </template>
        </div>
        <div class="message-time">
          {{ msg.sender == 'ai' ? msg.name : '' }}
          {{ formatTime(msg.timestamp) }}
        </div>
      </div>
    </div>

    <!-- 输入功能区 -->
    <div class="input-area">
      <el-input
        v-model="inputMessage"
        type="textarea"
        :rows="2"
        placeholder="输入消息..."
        @keyup.enter.exact="sendMessage"
      />
      <div class="action-bar">
        <el-input
          style="width: 30%; margin-right: 50%"
          type="text"
          v-model="to_name"
          clearable
        >
          <template #prepend>
            <el-select
              v-model="select"
              placeholder="客户端类型"
              style="width: 80px"
            >
              <el-option
                v-for="item in options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </template>
        </el-input>
        <input
          type="file"
          ref="fileInput"
          style="display: none"
          @change="handleFileUpload"
        />
        <!-- 上传进度条 -->
        <el-progress
          v-if="uploadProgress > 0"
          :percentage="uploadProgress"
          :status="uploadStatus"
          :color="progressColor"
          :stroke-width="16"
          style="margin-top: 0px"
        />
        <el-button type="primary" @click="sendFile" plain>选择文件</el-button>

        <el-button type="primary" @click="sendMessage" plain>发送</el-button>
      </div>
    </div>
  </div>
  <el-dialog v-model="dialogStatus" title="登陆二维码">
    <el-row>
      <el-input style="width: 60%" type="text" v-model="client_url" clearable>
        <template #prepend>
          <span>url</span>
        </template>
      </el-input>
    </el-row>

    <vue-qr
      :text="client_url"
      :size="500"
      :margin="10"
      :color-dark="'#000000'"
      @success="onGenerate"
    />
  </el-dialog>
  <el-dialog v-model="imgStatus" title="图片显示"></el-dialog>
</template>
  
  <script lang='ts' setup >
defineOptions({
  name: 'NimiChat',
})
import { ref, reactive, onMounted, watch, nextTick, onUnmounted } from 'vue'
//引入用户相关的小仓库
import useUserStore from '@/store/modules/user'
import useSocketClient from '@/store/socket/socket_client'
import emmit from '@/utils/emitter'
import { useRoute, useRouter } from 'vue-router'
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
import VueQr from 'vue-qr/src/packages/vue-qr.vue'
import { number } from 'echarts'
import { fi } from 'element-plus/es/locale'
import { CopyDocument } from '@element-plus/icons-vue'
import useClipboard from 'vue-clipboard3'
// import imageCompression from 'browser-image-compression'
// 挂载完毕
//onMounted(()=>{})
//监控数据
//watch(select, (newVal, oldVal) => {})
const user = useUserStore()
let dialogStatus = ref<boolean>(false)
let imgStatus = ref<boolean>(false)
let fileInput = ref<HTMLInputElement>()
let imgurl = ref('')
const client_url = ref('')
const qrCodeUrl = ref('')
const onGenerate = (qrUrl: any) => {
  console.log('生成成功:', qrUrl)
}
const url_to = () => {
  client_url.value = import.meta.env.VITE_CHAT_URL + user.username
  dialogStatus.value = true
  nextTick(() => {})
}
const client = useSocketClient()

let select = ref()

//选择器
let options = [
  {
    value: 'web',
    label: 'web',
  },
  {
    value: 'Client',
    label: 'Client',
  },
]
// 全局变量存储分片数据
const fileChunksMap = new Map()
emmit.on('chat', (data: any) => {
  // console.log(data)
  let temp_data = JSON.parse(data)
  // console.log(temp_data.msg)
  let msgObj = JSON.parse(temp_data.msg)
  // console.log(msgObj)
  if (msgObj.type === 'file') {
    handleFileChunk(msgObj, temp_data.name)
    // messages.push({
    //   sender: 'ai',
    //   type: 'file', // 新增: text/file
    //   content: msgObj.content,
    //   timestamp: new Date(),
    //   name: temp_data.name,
    //   // 文件消息新增字段
    //   fileName: msgObj.fileName,
    //   fileType: msgObj.fileType,
    // })
  } else {
    messages.push({
      sender: 'ai',
      type: 'text', // 新增: text/file
      content: msgObj.content,
      timestamp: new Date(),
      name: temp_data.name,
      // 文件消息新增字段
      fileName: '',
      fileType: '',
      url: '',
    })
  }

  scrollToBottom()
})
// let fileData,接收文件
function handleFileChunk(msgObj: any, name: any) {
  if (!msgObj.fileId) {
    console.log('http接收')
    //http接收
    messages.push({
      sender: 'ai',
      type: 'file',
      url: msgObj.url,
      fileName: msgObj.fileName,
      fileType: msgObj.fileType,
      timestamp: new Date(),
      content: '',
      name: name,
    })
    blobUrlStore.value.push(msgObj.url)
    // console.log(msgObj)
    setTimeout(scrollToBottom, 500)
    return
  }
  // console.log('websocket接收')
  // //方案一
  // const [fileSize, index, total, base64Data] = msgObj.content.split('|')
  // let size = Number(fileSize)
  // // 创建二进制存储
  // if (!fileChunksMap.has(msgObj.fileId)) {
  //   fileChunksMap.set(msgObj.fileId, {
  //     chunks: new Uint8Array(size), // 预分配空间
  //     received: 0,
  //     fileName: msgObj.fileName,
  //     fileType: msgObj.fileType,
  //   })
  // }

  // // 解码并存储分片
  // const byteArray = base64ToArrayBuffer(base64Data)
  // const store = fileChunksMap.get(msgObj.fileId)
  // store.chunks.set(byteArray, store.received)
  // store.received += byteArray.length

  // // 检查完成
  // if (store.received === size) {
  //   createFileFromChunks(store, name)
  // }
}

// Base64转ArrayBuffer
function base64ToArrayBuffer(base64: any) {
  const binaryString = window.atob(base64)
  const len = binaryString.length
  const bytes = new Uint8Array(len)
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i)
  }
  return bytes
}

// 从分片创建文件
function createFileFromChunks(store: any, name: any) {
  const blob = new Blob([store.chunks], { type: store.fileType })
  const url = URL.createObjectURL(blob)
  blobUrlStore.value.push(url)
  console.log('+++++++++++')
  console.log(url)
  messages.push({
    sender: 'ai',
    type: 'file',
    url: url, // 使用Blob URL
    fileName: store.fileName,
    fileType: store.fileType,
    timestamp: new Date(),
    content: '',
    name: name,
  })

  // 释放内存
  fileChunksMap.delete(store.fileId)
  setTimeout(scrollToBottom, 50)
  // // const { fileId, chunkIndex, totalChunks, content, fileName, fileType } =
  // //   msgObj
  // // 初始化存储结构
  // if (!fileChunksMap.has(fileId)) {
  //   fileChunksMap.set(fileId, {
  //     chunks: new Array(totalChunks),
  //     receivedCount: 0,
  //     fileName,
  //     fileType,
  //   })
  // }

  // let fileData = fileChunksMap.get(fileId)

  // // 存储分片
  // fileData.chunks[chunkIndex] = content
  // fileData.receivedCount++

  // // 检查是否接收完成
  // if (fileData.receivedCount === totalChunks) {
  //   for (let index = 0; index < fileData.chunks.length; index++) {
  //     const element = fileData.chunks[index]
  //     console.log('收到分片数据')
  //     console.log(element)
  //   }
  //   // 合并所有分片
  //   const fullBase64 = fileData.chunks.join('')
  //   const fullMessage = {
  //     type: 'file',
  //     content: fullBase64,
  //     fileName: fileData.fileName,
  //     fileType: fileData.fileType,
  //     timestamp: new Date(),
  //   }
  //   console.log('收到数据')
  //   console.log(fullBase64)
  //   // 添加消息到展示区
  //   // addMessageForFile(fullMessage, sender, name);
  //   messages.push({
  //     sender: 'ai',
  //     type: 'file', // 新增: text/file
  //     content: fullBase64,
  //     timestamp: new Date(),
  //     name: name,
  //     // 文件消息新增字段
  //     fileName: fileData.fileName,
  //     fileType: fileData.fileType,
  //   })
  //   // 清理缓存
  //   fileChunksMap.delete(fileId)
  // }
}
let to_name = ref(user.username)
// 消息数据
const messages = reactive([
  {
    sender: 'ai',
    type: 'text', // 新增: text/file
    content: '你好！我是聊天助手，可以帮你传递信息',
    timestamp: new Date(),
    name: 'admin',
    url: '',
    // 文件消息新增字段
    fileName: '',
    fileType: '',
  },
])
//复制
const { toClipboard } = useClipboard()
// 写入文本到剪贴板
async function copyToClipboard(text: any) {
  try {
    await toClipboard(text)
    ElMessage.success('复制成功')
  } catch (err) {
    console.error('复制失败:', err)
    // 降级到旧方法
    fallbackCopy(text)
  }
}
function fallbackCopy(text: any) {
  const textarea = document.createElement('textarea')
  textarea.value = text
  textarea.style.position = 'fixed' // 避免页面滚动
  document.body.appendChild(textarea)
  textarea.select()

  try {
    const success = document.execCommand('copy')
    if (!success) throw new Error('复制失败')
    ElMessage.success('复制成功(旧)')
    console.log('复制成功（旧方法）')
  } catch (err) {
    ElMessage.error('复制失败')
    console.error('复制失败:', err)
  } finally {
    document.body.removeChild(textarea)
  }
}

const inputMessage = ref('')
// type MessageContainerRef = { scrollTop: number; scrollHeight: number }
const messageContainer = ref<any | null>(null)
// const messageContainer = ref(null)
// console.log()
// 发送消息
const sendMessage = async () => {
  // console.log(select.value.value)
  if (!inputMessage.value.trim()) return

  // 添加用户消息
  messages.push({
    sender: 'user',
    type: 'text', // 新增: text/file
    content: inputMessage.value.trim(),
    timestamp: new Date(),
    name: '',
    // 文件消息新增字段
    fileName: '',
    fileType: '',
    url: '',
  })
  const textMessage = {
    type: 'text',
    content: inputMessage.value.trim(),
  }
  for (let index = 0; index < 20; index++) {
    if (client.socket_obj?.readyState === WebSocket.OPEN) {
      break
    } else {
      console.log(index)
      await sleep(1)
    }
  }
  console.log('等待结束')
  // 发送socket
  let client_status = client.socket_obj?.send(
    JSON.stringify(
      client.IOC_res_success(
        {
          to_name: to_name.value,
          name: user.username,
          type: select.value.value,
        },
        'chat',
        JSON.stringify(textMessage),
      ),
    ),
  )

  // 清空输入框
  inputMessage.value = ''

  // 模拟AI回复
  //   setTimeout(() => {
  //     messages.push({
  //       sender: 'ai',
  //       content: `收到您的消息: "${
  //         messages[messages.length - 1].content
  //       }"，我正在处理中...`,
  //       timestamp: new Date(),
  //       name: 'admin',
  //     })
  //     scrollToBottom()
  //   }, 800)

  scrollToBottom()
}

//进度条
const uploadProgress = ref(0)
const uploadStatus = ref('')
const fileUrl = ref('')
const progressColor = ref('#409eff')
// 新增文件处理方法
const blobUrlStore = ref<String[]>([]) //url集合
const handleFileUpload = (e: Event) => {
  // console.log(111111111)
  const file = (e.target as HTMLInputElement).files?.[0]
  if (!file) return
  // console.log(file.type.startsWith('image'))
  if (file.size > 200 * 1024 * 1024) {
    ElMessage.info('文件大于200M')
    ;(e.target as HTMLInputElement).value = '' // 清空选择
    return
  }
  const reader = new FileReader()
  reader.onload = () => {
    // const base64Data = reader.result as string
    const bytes = new Uint8Array(reader.result as ArrayBuffer)
    const blob = new Blob([bytes], { type: file.type })
    const url = URL.createObjectURL(blob)
    console.log(url)
    blobUrlStore.value.push(url)
    // 构建文件消息
    const fileMessage = {
      sender: 'user',
      type: 'file',
      content: '', // 去除data:前缀
      fileName: file.name,
      fileType: file.type,
      timestamp: new Date(),
      name: '',
      url: url,
    }

    messages.push(fileMessage)
    setTimeout(scrollToBottom, 50)

    // // 通过WebSocket发送
    // client.socket_obj?.send(
    //   JSON.stringify(
    //     client.IOC_res_success(
    //       {
    //         to_name: to_name.value,
    //         name: user.username,
    //         type: select.value.value,
    //       },
    //       'chat',
    //       JSON.stringify(fileMessage), // 发送整个消息对象
    //     ),
    //   ),
    // )
  }
  nextTick(() => {
    ;(e.target as HTMLInputElement).value = ''
  })
  reader.readAsArrayBuffer(file)

  //压缩图片
  // const options = {
  //   maxSizeMB: 1, // 压缩后文件大小限制（MB）
  //   maxWidthOrHeight: 1920, // 最大宽高
  //   useWebWorker: true, // 使用 Web Worker 提升性能
  // }

  // try {
  //   console.log(file.size)
  //   const compressedFile = await imageCompression(file, options)
  //   console.log(compressedFile.size)
  // } catch (error) {
  //   console.error('压缩失败:', error)
  //   ElMessage.error('图片压缩失败，请重试')
  // }
  console.log('文件大小', file.size)
  if (file.size < 200 * 1024 * 1024) {
    //oss上传
    console.log('http上传')
    uploadProgress.value = 0
    uploadStatus.value = ''
    const formData = new FormData()
    formData.append('file', file)
    // 使用 axios 发起 POST 请求
    request
      .post('/all/upload/', formData, {
        timeout: 600000,
        onUploadProgress: (progressEvent: any) => {
          uploadProgress.value = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total,
          )
        },
      })
      .then(async (response: any) => {
        fileUrl.value = response.data.url
        console.log(fileUrl.value)
        uploadStatus.value = 'success'
        ElMessage.success('上传成功')
        progressColor.value = '#67c23a' // 上传成功变绿色
        // 构建分片消息
        const chunkMessage = {
          sender: 'user',
          type: 'file',
          content: '',
          fileName: file.name,
          fileType: file.type,
          timestamp: new Date(),
          url: fileUrl.value,
        }
        for (let index = 0; index < 20; index++) {
          if (client.socket_obj?.readyState === WebSocket.OPEN) {
            break
          } else {
            console.log(index)
            await sleep(1)
          }
        }
        console.log('等待结束')
        // 发送消息
        client.socket_obj?.send(
          JSON.stringify(
            client.IOC_res_success(
              {
                to_name: to_name.value,
                name: user.username,
                type: select.value.value,
              },
              'chat',
              JSON.stringify(chunkMessage), // 发送整个消息对象
            ),
          ),
        )
      })
      .catch((error: any) => {
        uploadStatus.value = 'exception'
        ElMessage.error('上传失败')
        progressColor.value = '#f56c6c' // 上传失败变红色
      })
    return
  }
  // console.log('websocket上传')
  // //发送代码
  // uploadProgress.value = 0
  // uploadStatus.value = ''
  // const CHUNK_SIZE = 64 * 1024 // 64KB分片大小
  // const fileId = Date.now() + '-' + Math.random().toString(36).substr(2, 9)
  // const totalChunks = Math.ceil(file.size / CHUNK_SIZE)
  // let chunkIndex = 0

  // const sendNextChunk = () => {
  //   if (chunkIndex >= totalChunks) {
  //     uploadStatus.value = 'success'
  //     ElMessage.success('上传成功')
  //     progressColor.value = '#67c23a' // 上传成功变绿色
  //     // 所有分片发送完成
  //     console.log('文件发送完成')
  //     return
  //   }

  //   const start = chunkIndex * CHUNK_SIZE
  //   const end = Math.min(start + CHUNK_SIZE, file.size)
  //   const chunk = file.slice(start, end)

  //   const reader = new FileReader()
  //   reader.onload = async function () {
  //     // const base64Data = (reader.result as string).split(',')[1]
  //     const bytes = new Uint8Array(reader.result as ArrayBuffer)
  //     const base64Data = arrayBufferToBase64(bytes)
  //     console.log('分片数据')
  //     // 构建分片消息
  //     const chunkMessage = {
  //       fileId,
  //       chunkIndex,
  //       totalChunks,
  //       sender: 'user',
  //       type: 'file',
  //       content: `${file.size}|${chunkIndex}|${totalChunks}|${base64Data}`,
  //       fileName: file.name,
  //       fileType: file.type,
  //       timestamp: new Date(),
  //     }
  //     for (let index = 0; index < 20; index++) {
  //       if (client.socket_obj?.readyState === WebSocket.OPEN) {
  //         break
  //       } else {
  //         console.log(index)
  //         await sleep(1)
  //       }
  //     }
  //     // 发送消息
  //     client.socket_obj?.send(
  //       JSON.stringify(
  //         client.IOC_res_success(
  //           {
  //             to_name: to_name.value,
  //             name: user.username,
  //             type: select.value.value,
  //           },
  //           'chat',
  //           JSON.stringify(chunkMessage), // 发送整个消息对象
  //         ),
  //       ),
  //     )
  //     // client.socket_obj?.send(
  //     //   JSON.stringify({
  //     //     code: 200,
  //     //     msg: JSON.stringify(chunkMessage),
  //     //     ioc_name: 'chat',
  //     //     data: {
  //     //       to_name: to_name.value,
  //     //       name: user.username,
  //     //       type: select.value.value,
  //     //     },
  //     //   }),
  //     // )

  //     chunkIndex++
  //     uploadProgress.value = Math.round((chunkIndex * 100) / totalChunks)
  //     setTimeout(sendNextChunk, 50) // 添加延迟避免阻塞
  //   }

  //   reader.readAsArrayBuffer(chunk)
  // }

  // sendNextChunk()
}

function sleep(time: number) {
  return new Promise((resolve) => setTimeout(resolve, time * 1000))
}
function arrayBufferToBase64(buffer: any) {
  let binary = ''
  const bytes = new Uint8Array(buffer)
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i])
  }
  return window.btoa(binary)
}
const sendFile = () => {
  fileInput.value?.click()
}
// 判断是否为图片文件
const isImageFile = (fileType: any) => {
  return fileType.startsWith('image/')
}

// 获取图片的DataURL
const getImageDataURL = (msg: any) => {
  console.log('图片url')
  // console.log(`data:${msg.fileType};base64,${msg.content}`)
  return `data:${msg.fileType};base64,${msg.content}`
}

// 显示全屏图片预览
// const showFullImagePreview = (msg: any) => {
//   imgurl.value
// }

// 滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    if (messageContainer.value) {
      messageContainer.value.scrollTop = messageContainer.value.scrollHeight
    }
  })
}
let observer: MutationObserver | null = null

// const initScrollObserver = () => {
//   if (!messageContainer.value) return

//   observer = new MutationObserver(() => {
//     scrollToBottom() // 调用上述滚动函数
//   })

//   observer.observe(messageContainer.value, {
//     childList: true, // 监听子元素变化
//     subtree: true, // 监听后代元素
//   })
// }
// 时间格式化
const formatTime = (date: any) => {
  return `${date.getHours().toString().padStart(2, '0')}:${date
    .getMinutes()
    .toString()
    .padStart(2, '0')}`
}

// 初始化时滚动到底部
onMounted(() => {
  // initScrollObserver()
  scrollToBottom()
})
onMounted(() => {
  select.value = options[1]
})
// 组件卸载时释放Blob URL
onUnmounted(() => {
  // observer?.disconnect()
  messages.forEach((msg) => {
    if (msg.type === 'file' && msg.url) {
      URL.revokeObjectURL(msg.url)
    }
  })
})
</script>
  <style scope lang='scss'>
.chat-container {
  display: flex;
  flex-direction: column;
  height: 80vh;
  max-width: 800px;
  margin: 0 auto;
  // border: 1px solid #ebeef5;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.message-display {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  // background-color: #f8f9fa;
}

.message-bubble {
  margin-bottom: 15px;
  max-width: 80%;
}

.message-bubble.user {
  margin-left: auto;
}

.message-bubble.ai {
  margin-right: auto;
}

.bubble-content {
  padding: 12px 16px;
  border-radius: 18px;
  line-height: 1.5;
  position: relative;
}

.message-bubble.user .bubble-content {
  background-color: #409eff;
  // color: white;
  border-bottom-right-radius: 4px;
}

.message-bubble.ai .bubble-content {
  // background-color: #f0f2f5;
  // color: #303133;
  border-bottom-left-radius: 4px;
}

.message-time {
  font-size: 12px;
  color: #909399;
  margin-top: 4px;
  text-align: right;
}

.message-bubble.ai .message-time {
  text-align: left;
}

.input-area {
  //border-top: 1px solid #ebeef5;
  padding: 15px;
  // background-color: white;
}

.action-bar {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}
</style>